package org.atlantis.datasink.cfg;

import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.atlantis.datasink.codec.CodecOption;
import org.atlantis.datasink.codec.CommandFactory;
import org.atlantis.datasink.server.CommType;
import org.atlantis.datasink.utils.Utils;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

class CodecOptionFragment extends AbstractOptionFragment<CodecOption> {

	private static final String SERVER_NAME_ATTR = "server-name";
	private static final String TIMEOUT_ATTR = "timeout";
	private static final String BIND_PORT_TAG = "bind-port";
	private static final String REQUEST_SERVERS_TAG = "request-servers";
	private static final String COMMAND_SEND_INTERVAL_TAG = "command-send-interval";
	private static final String COMMAND_FACTORY_TAG = "command-factory";

	@Override
	public CodecOption parse(Element optElm) throws Exception {
		CodecOptionImpl codecOpt = new CodecOptionImpl();
		codecOpt.name = optElm.getAttribute(NAME_ATTR);
		codecOpt.serverName = optElm.getAttribute(SERVER_NAME_ATTR);

		if (optElm.hasAttribute(TIMEOUT_ATTR)) codecOpt.timeout = Long.parseLong(optElm.getAttribute(TIMEOUT_ATTR));

		NodeList bindPortList = optElm.getElementsByTagName(BIND_PORT_TAG);
		if (bindPortList.getLength() > 0) codecOpt.bindPort = Integer.parseInt(bindPortList.item(0).getFirstChild()
				.getNodeValue());

		NodeList commandFactoryList = optElm.getElementsByTagName(COMMAND_FACTORY_TAG);
		if (commandFactoryList.getLength() > 0) codecOpt.requestFactory = newByClass(commandFactoryList.item(0).getFirstChild()
				.getNodeValue(), codecOpt);

		NodeList commandSendIntervalList = optElm.getElementsByTagName(COMMAND_SEND_INTERVAL_TAG);
		if (commandSendIntervalList.getLength() > 0) codecOpt.commandSendInterval = Long.parseLong(commandSendIntervalList
				.item(0).getFirstChild().getNodeValue());

		NodeList requestServersList = optElm.getElementsByTagName(REQUEST_SERVERS_TAG);
		if (requestServersList.getLength() > 0) {
			String serverStrs[] = requestServersList.item(0).getFirstChild().getNodeValue().trim().split("\\n");
			for (String serverStr : serverStrs) {
				codecOpt.requestServers.add(Utils.toSocketAddress(serverStr.trim()));
			}
		}

		NodeList extendsList = optElm.getElementsByTagName(EXTENDS_TAG);
		if (extendsList.getLength() > 0) codecOpt.extendMap = parseExtend((Element) extendsList.item(0));
		return codecOpt;
	}

	private CommandFactory newByClass(String className, CodecOption codecOpt) throws Exception {
		Class<?> commandFactoryClass = Class.forName(className);
		CommandFactory commandFactory = (CommandFactory) commandFactoryClass.newInstance();
		commandFactory.bindOption(codecOpt);
		return commandFactory;
	}

	private static class CodecOptionImpl implements CodecOption {

		Map<String, String> extendMap = new HashMap<String, String>();
		String name;
		String serverName;
		long timeout = -1;
		int bindPort = -1;
		CommandFactory requestFactory;
		long commandSendInterval = -1L;
		List<SocketAddress> requestServers = new ArrayList<SocketAddress>();

		@Override
		public String getExtend(String key) {
			return extendMap.get(key);
		}

		@Override
		public String getName() {
			return name;
		}

		@Override
		public String getServerName() {
			return serverName;
		}

		@Override
		public long getTimeout() {
			return timeout;
		}

		@Override
		public int getBindPort() {
			return bindPort;
		}

		@Override
		public CommandFactory getRequestFactory() {
			return requestFactory;
		}

		@Override
		public long getCommandSendInterval() {
			return commandSendInterval;
		}

		@Override
		public List<SocketAddress> getRequestServers() {
			return requestServers;
		}

		@Override
		public void checkByCommType(CommType commType) {
			if (commType == CommType.ACTIVE) {
				if (requestFactory == null || commandSendInterval <= 0 || requestServers.isEmpty()) throw new IllegalArgumentException(
						name + " is binding to the " + commType.toString()
								+ " server, but it's requestFactory or commandSendInterval or requestServers is empty.");
			} else {
				if (bindPort <= 0) throw new IllegalArgumentException(name + " is binding to the " + commType.toString()
						+ " server, but it's bindPort is empty.");
			}
		}
	}
}
